/******************************************************************************
 * 
 * Announce: CSharpKit, Basic algorithms, components and definitions.
 *           Copyright (C) ShenYongchen.
 *           All rights reserved.
 *   Author: 申永辰.郑州 (shenyczz@163.com)
 *  WebSite: http://github.com/shenyczz/CSharpKit
 *
 * THIS CODE IS LICENSED UNDER THE MIT LICENSE (MIT).
 * THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF 
 * ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
 * IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
 * PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
 * 
******************************************************************************/

using System;
using System.Collections.Generic;

namespace CSharpKit.Numerics.LinearRegression
{
    /// <summary>
    /// 名称：简单的一元线性回归：y:x -> a+b*x
    /// 依赖: NONE
    /// </summary>
    public static class SimpleRegression
    {
        /// <summary>
        /// 最小二乘拟合 y = a+b*x <br/>
        /// </summary>
        /// <param name="x">Predictor(自变量)</param>
        /// <param name="y">Response (因变量)</param>
        /// <returns>
        /// 返回二元数组 Tuple：<br/>
        /// Item1：intercept（截距 a）<br/>
        /// Item2：slope（斜率 b）<br/>
        /// </returns>
        public static Tuple<double, double> Fit(double[] x, double[] y)
        {
            if (x.Length != y.Length)
            {
                throw new ArgumentException(string.Format(Resources.Numerics.SampleVectorsSameLength, x.Length, y.Length));
            }

            if (x.Length <= 1)
            {
                throw new ArgumentException(string.Format(Resources.Numerics.RegressionNotEnoughSamples, 2, x.Length));
            }

            // 1. 求 x,y 的平均值
            double xmean = 0.0;
            double ymean = 0.0;
            for (int i = 0; i < x.Length; i++)
            {
                xmean += x[i];
                ymean += y[i];
            }

            xmean /= x.Length;
            ymean /= y.Length;

            // 2. 求协方差和方差
            double covariance = 0.0;
            double variance = 0.0;
            for (int i = 0; i < x.Length; i++)
            {
                double xdiff = x[i] - xmean;
                double ydiff = y[i] - ymean;

                covariance += xdiff * ydiff;    // 协方差
                variance += xdiff * xdiff;      // 方差
            }

            // 3. 求斜率和截距
            var slope = covariance / variance;      // 斜率
            var intercept = ymean - slope * xmean;  // 截距

            return new Tuple<double, double>(intercept, slope);
        }

        /// <summary>
        /// 通过原点的直线<br/>
        /// Least-Squares fitting the points (x,y) to a line f(x) -> b*x,
        /// returning its best fitting parameter b,
        /// where the intercept is zero and b the slope.
        /// </summary>
        /// <param name="x">Predictor (independent)</param>
        /// <param name="y">Response (dependent)</param>
        public static double FitThroughOrigin(double[] x, double[] y)
        {
            if (x.Length != y.Length)
            {
                throw new ArgumentException(string.Format(Resources.Numerics.SampleVectorsSameLength, x.Length, y.Length));
            }

            if (x.Length <= 1)
            {
                throw new ArgumentException(string.Format(Resources.Numerics.RegressionNotEnoughSamples, 2, x.Length));
            }

            double mxy = 0.0;
            double mxx = 0.0;
            for (int i = 0; i < x.Length; i++)
            {
                mxx += x[i] * x[i];
                mxy += x[i] * y[i];
            }

            return mxy / mxx;
        }












        /// <summary>
        /// Least-Squares fitting the points (x,y) to a line y : x -> a+b*x,
        /// returning its best fitting parameters as (a, b) tuple,
        /// where a is the intercept and b the slope.
        /// </summary>
        /// <param name="samples">Predictor-Response samples as tuples</param>
        public static Tuple<double, double> Fit(IEnumerable<Tuple<double, double>> samples)
        {
            var xy = samples.UnpackSinglePass();
            return Fit(xy.Item1, xy.Item2);
        }

        /// <summary>
        /// Least-Squares fitting the points (x,y) to a line y:x -> b*x <para/>
        /// returning its best fitting parameter b,
        /// where the intercept is zero and b the slope.
        /// </summary>
        /// <param name="samples">Predictor-Response samples as tuples</param>
        /// <returns>b: 斜率</returns>
        public static double FitThroughOrigin(IEnumerable<Tuple<double, double>> samples)
        {
            double mxy = 0.0;
            double mxx = 0.0;

            foreach (var sample in samples)
            {
                mxx += sample.Item1 * sample.Item1;
                mxy += sample.Item1 * sample.Item2;
            }

            return mxy / mxx;
        }

        //}}@@@
    }




}
